# 定制类
'''
看到类似__slots__ 这种形如 __xxx__的变量或者函数名就要注意，这些在python中是有特殊用途的。__slots__我们已经知道怎么用了，__len()__方法我们
也知道是为了能让class作用于 len() 函数，除此之外，Python 的class中还有许多这样有特殊用途的函数，可以帮助我们定制类
'''


# __str__
# 我们先定义一个 Student 类，打印一个实例：
class Student(object):
    def __init__(self, name):
        self.name = name

    # 打印类实例的时候会自动调用这个方法
    def __str__(self):
        return 'Student object (name: %s)' % self.name

    __repr__ = __str__


print(Student('Michael'))  # 输出：Student object (name: Michael)

# __iter__
'''
如果一个类想被用于 for ... in 循环，类似list或tuple那样，就必须实现一个 __iter__()方法，该方法返回一个迭代对象，然后，python的for循环就会不断调用
该迭代对象的__next__() 方法拿到循环的下一个值，知道遇到 stopInteration错误时退出循环
'''


# 我们以斐波那契数列威力，写一个Fib类，可以作用于for循环：
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1  # 初始化两个计数器a,b

    def __iter__(self):
        return self  # 实例本身就是迭代对象，故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > 100000:  # 退出循环的条件
            raise StopIteration()
        return self.a  # 返回下一个值


for i in Fib():
    print(i)

# __getitem__
'''
Fib实例虽然能用作于for循环，看起来和list有点像，但是，把它当成list来使用还是不行，比如，取第5个元素，就无法实现，这时就需要用到__getitem__取实现
'''

print('-' * 100)
# 要表现得像list那样按照下标取出元素，需要实现__getitem__()方法：
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
f = Fib()
print(f[0])
print(f[5])
print(f[10])


# __getattr__
'''
正常情况下，当我们调用类的方法或属性时，如果不存在，就会报错
当调用不存在的属性时，比如score，Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性
'''

#__call__
'''
当一个实例被当成方法调用的时候，会自动调用 __call__()方法
'''